/**
 * Copyright (C) 2008 by The Regents of the University of California
 * Redistribution of this file is permitted under the terms of the GNU
 * Public License (GPL).
 *
 * @author Junghoo "John" Cho <cho AT cs.ucla.edu>
 * @date 3/24/2008
 */

#include <cstdio>
#include <iostream>
#include <fstream>
#include <string>
#include "Bruinbase.h"
#include "SqlEngine.h"

using namespace std;

// external functions and variables for load file and sql command parsing 
extern FILE* sqlin;
int sqlparse(void);


RC SqlEngine::run(FILE* commandline)
{
  fprintf(stdout, "Bruinbase> ");

  // set the command line input and start parsing user input
  sqlin = commandline;
  sqlparse();  // sqlparse() is defined in SqlParser.tab.c generated from
               // SqlParser.y by bison (bison is GNU equivalent of yacc)

  return 0;
}

RC SqlEngine::select(int attr, const string& table, const vector<SelCond>& cond)
{
  RecordFile rf;   // RecordFile containing the table
  RecordId   rid;  // record cursor for table scanning

  RC     rc;
  int    key;     
  string value;
  int    count;
  int    diff;

  // open the table file
  if ((rc = rf.open(table + ".tbl", 'r')) < 0) {
    fprintf(stderr, "Error: table %s does not exist\n", table.c_str());
    return rc;
  }
	
	BTreeIndex bIndex;
	bool index=false;
	if (bIndex.open(table+".idx", 'r')==0) 
	{
		index=true;
	}
	
	int searchKey;
	for (unsigned i = 0; i < cond.size(); i++) {
		
		if (index)
		{
			if (cond[i].attr==1) 
			{
				searchKey=atoi(cond[i].value);
				IndexCursor cursor;
				if (cond[i].comp==SelCond::EQ) 
				{
					cout << "search key " << searchKey << endl;
					bIndex.locate(searchKey, cursor);
					bIndex.readForward(cursor, key, rid);
					rf.read(rid, key, value);
					switch (attr) 
					{
						case 1:  // SELECT key
							fprintf(stdout, "%d\n", key);
							break;
						case 2:  // SELECT value
							fprintf(stdout, "%s\n", value.c_str());
							break;
						case 3:  // SELECT *
							fprintf(stdout, "%d '%s'\n", key, value.c_str());
							break;
					}
				}
				
				if (cond[i].comp==SelCond::GT) 
				{
					cout << "does this GT work" <<endl;
					bIndex.locate(searchKey+1, cursor);
					bIndex.readForward(cursor, key, rid);
					while (key>searchKey)
					{
						rf.read(rid, key, value);
						switch (attr) 
						{
							case 1:  // SELECT key
								fprintf(stdout, "%d\n", key);
								break;
							case 2:  // SELECT value
								fprintf(stdout, "%s\n", value.c_str());
								break;
							case 3:  // SELECT *
								fprintf(stdout, "%d '%s'\n", key, value.c_str());
								break;
						}
						bIndex.readForward(cursor, key, rid);
					}
				}
				/*
				 case SelCond::LT:
				 if (diff >= 0) 
				 break;
				 case SelCond::GE:
				 if (diff < 0)
				 break;
				 case SelCond::LE:
				 if (diff > 0)
				 break;
				 }
				 */
				cout << endl;
				
			}
		}	
	}
	

  // scan the table file from the beginning
  rid.pid = rid.sid = 0;
  count = 0;
  while (rid < rf.endRid()) {
    // read the tuple
    if ((rc = rf.read(rid, key, value)) < 0) {
      fprintf(stderr, "Error: while reading a tuple from table %s\n", table.c_str());
      goto exit_select;
    }

    // check the conditions on the tuple
    for (unsigned i = 0; i < cond.size(); i++) {
      // compute the difference between the tuple value and the condition value
      switch (cond[i].attr) {
      case 1:
	diff = key - atoi(cond[i].value);
	break;
      case 2:
	diff = strcmp(value.c_str(), cond[i].value);
	break;
      }

      // skip the tuple if any condition is not met
      switch (cond[i].comp) {
      case SelCond::EQ:
	if (diff != 0) goto next_tuple;
	break;
      case SelCond::NE:
	if (diff == 0) goto next_tuple;
	break;
      case SelCond::GT:
	if (diff <= 0) goto next_tuple;
	break;
      case SelCond::LT:
	if (diff >= 0) goto next_tuple;
	break;
      case SelCond::GE:
	if (diff < 0) goto next_tuple;
	break;
      case SelCond::LE:
	if (diff > 0) goto next_tuple;
	break;
      }
    }

    // the condition is met for the tuple. 
    // increase matching tuple counter
    count++;

    // print the tuple 
    switch (attr) {
    case 1:  // SELECT key
      fprintf(stdout, "%d\n", key);
      break;
    case 2:  // SELECT value
      fprintf(stdout, "%s\n", value.c_str());
      break;
    case 3:  // SELECT *
      fprintf(stdout, "%d '%s'\n", key, value.c_str());
      break;
    }

    // move to the next tuple
    next_tuple:
    ++rid;
  }

  // print matching tuple count if "select count(*)"
  if (attr == 4) {
    fprintf(stdout, "%d\n", count);
  }
  rc = 0;

  // close the table file and return
  exit_select:
  rf.close();
  return rc;
}

RC SqlEngine::load(const string& table, const string& loadfile, bool index)
{
  /* your code here */
	
	//always set index to true for now.
	//index=true;
	
	string line;
	//cout << "the load function was called" << endl;
	//const char* filename=(loadfile.c_str());
	//string filename=loadfile;
	
	
	RecordFile rf;   // RecordFile containing the table
	RecordId   rid;  // record cursor for table scanning
	
	RC     rc;
	int    key;     
	string value;
	int    count;
	int    diff;
	
	// open the table file
	if ((rc = rf.open(table + ".tbl", 'w')) < 0) 
	{
		fprintf(stderr, "Error: table %s does not exist\n", table.c_str());
		return rc;
	}
	rid.pid = rid.sid = 0;
	count = 0;
	
	BTreeIndex bIndex;
	if (index)
	{
		if ((rc = bIndex.open(table+".idx", 'w')) < 0) 
		{
			fprintf(stderr, "Error: index %s does not exist\n", table.c_str());
			return rc;
		}
	}
	
	
	ifstream lfile (loadfile.c_str());
	
	if(lfile.is_open())
	{
		while (lfile.good())
			{
				getline (lfile,line);
				int sep=0;
				//cout << line[sep] << line.at(sep) << endl;
				
				for (int i=0; i < line.size(); i++)
				{
					//cout << line[i] << line.at(i) << endl;
					
					if (line[i]==',')
					{
						sep=i;
					}
				}
				if (sep==0)
				{
					//cout << "error in comma search" << endl;
					break;
				}
				string id = line.substr(0,sep);
				int mid=atoi(id.c_str());
				RecordId end=rf.endRid();
				string name = line.substr(sep+2,line.size()-(sep+3));
				
				if (index)
				{
					
					if ((rc = bIndex.insert(mid, end)) < 0) 
					{
						fprintf(stderr, "Error: while writing a tuple from index %s\n", table.c_str());
						goto exit_load;
					}
					
				}
				
				// read the tuple
				if ((rc = rf.append(mid, name, end)) < 0) 
				{
					fprintf(stderr, "Error: while writing a tuple from table %s\n", table.c_str());
					goto exit_load;
				}
				
				//cout << id << " " << name << endl;
				
				//cout << line << endl;
			}
				exit_load:
			   lfile.close();
			   
	}

  return 0;
}

RC SqlEngine::parseLoadLine(const string& line, int& key, string& value)
{
    const char *s;
    char        c;
    string::size_type loc;
    
    // ignore beginning white spaces
    c = *(s = line.c_str());
    while (c == ' ' || c == '\t') { c = *++s; }

    // get the integer key value
    key = atoi(s);

    // look for comma
    s = strchr(s, ',');
    if (s == NULL) { return RC_INVALID_FILE_FORMAT; }

    // ignore white spaces
    do { c = *++s; } while (c == ' ' || c == '\t');
    
    // if there is nothing left, set the value to empty string
    if (c == 0) { 
        value.erase();
        return 0;
    }

    // is the value field delimited by ' or "?
    if (c == '\'' || c == '"') {
        s++;
    } else {
        c = '\n';
    }

    // get the value string
    value.assign(s);
    loc = value.find(c, 0);
    if (loc != string::npos) { value.erase(loc); }

    return 0;
}
